
#include "memalloc.hpp"


char* MemAllocImpl::SwapToMem(SmartPtrImpl * ptr) {

	if (mem_free < ptr->size){
		throw MemAllocException(MemAllocException::E_NOMEM, "mem is full");
	}
	uint64_t swap_page_num = ptr->offset * min_chunk / PAGESIZE;
	if (count_page_swap[swap_page_num].mem_page == -1){
		//page is not mapped
		count_page_swap[swap_page_num].mem_page = FindMem(ptr->page_size_num);
		ptr->pData = mem + PAGESIZE * count_page_swap[swap_page_num].mem_page + (min_chunk * ptr->offset) % PAGESIZE;

		//do mmapping, taking count_page_swap[...].get_count into account !!!
#ifdef WINDOWS
		fseek(pFile, swap_page_num * PAGESIZE, SEEK_SET);
		fread(mem + PAGESIZE*count_page_swap[swap_page_num].mem_page, 1,
			PAGESIZE << ptr->page_size_num, pFile);
#endif

#ifdef LINUX			
		void * ret_value = mmap(mem + PAGESIZE*count_page_swap[swap_page_num].mem_page,
                        PAGESIZE << ptr->page_size_num,
                        PROT_READ | PROT_WRITE,  
                        MAP_FIXED|MAP_SHARED,
                        dFile,
                        swap_page_num * PAGESIZE);

                if (ret_value == (void*)-1){
                    switch (errno){
                        case ENOMEM: 
                            printf("ENOMEM\n"); break;
                        case EAGAIN:
                            printf("EAGAIN\n"); break;
                        default:
                            perror("ooops");
                        
                    }
                }
#endif
	}
	else{
		//page is mapped
		ptr->pData = mem + PAGESIZE * count_page_swap[swap_page_num].mem_page
			+ (min_chunk * ptr->offset) % PAGESIZE;
	}
	


	ptr->state = SmartPtrImpl::IN_MEM;

	return ptr->pData;
}


//return relative address after mem in pages to the corresponding page (pages)
size_t MemAllocImpl::FindMem(int sz_num){
	size_t page_num = free_mem_pages[sz_num].Pop();
	//make a sufficient block in mem, unfree and return it
	if (page_num == -1){
		int i = sz_num;
		while (i < N_mem && page_num == -1){
			i++;
			page_num = free_mem_pages[i].Pop();
		}

		if (page_num != -1){
			//have managed to find enough completely free space in a bigger chunk
			while (i > sz_num){
				List<size_t>::Merge(free_mem_pages, i - 1, N_mem, page_num);
				page_num = BUDDY(page_num, i - 1);
				i--;
			}
			return page_num;
		}
		else{
			//have to take over some released space
			i = sz_num;
                        page_num = released_mem_pages[i].Pop(i);
			if (page_num == -1){
				int i = sz_num;
				while (i < N_mem && page_num == -1){
					i++;
					page_num = released_mem_pages[i].Pop(i);
                                        while (page_num != -1){
                                            page_num = released_mem_pages[i].Pop(i);
                                        }
				}
				if (page_num != -1){
					//send one chunk out of mem and tear it's place in peaces

					while (i > sz_num){
						List<size_t>::Merge(free_mem_pages, i - 1, N_mem, page_num);
						page_num = BUDDY(page_num, i - 1);
						i--;
					}

					//now desired chunk remained in chunk_num
					return page_num;
				}
				else{
					//smaller released chunks are the last resort
					//TODO merge!!!
					throw MemAllocException(MemAllocException::E_2BIG, "Fragmentation of mem!");
				}
			}
			else {
				return page_num;
			}
		}//if(chunk_num != -1) else{...
	}//if (chunk_num == -1){...
	else{
		return page_num;
	}

}
